<!DOCTYPE html>
<html lang="en">

<head>
	<title>MediaRecorder录制二进制音视频流，MediaSource附加到video上实时播放</title>
</head>

<body>
	<button id="start">start</button>
	<button id="pause">pause</button>
	<button id="resume">resume</button>
	<button id="stop">stop</button>
	<audio id="audio1" controls style="display: none;"></audio>
	<video id="video1" width="300" height="150" autoplay="false" controls></video>
	<span id="value1"></span>
	<script>
		var _mediaRecorder;
		var _stream;

		var mimeCodec = 'video/webm; codecs=opus,vp8';
		//var mimeCodec = 'audio/webm;codecs=opus';
		var mediaSource;
		var sourceBuffer;
		var queue = [];
		var video1 = document.getElementById("video1");

		document.getElementById("start").onclick = () => {
			if (navigator.mediaDevices) {
				console.log('getUserMedia supported.');

				navigator.mediaDevices.getUserMedia({ audio: true, video: true })
					.then((stream) => {
						_stream = stream;
						_mediaRecorder = new MediaRecorder(stream);

						if (_mediaRecorder.state == 'inactive') {
							createMediaSource();
							_mediaRecorder.start(10); // 10毫秒触发一次ondataavailable
						}
						console.log("recorder started", _mediaRecorder.state);
						_mediaRecorder.onstop = (e) => {
							console.log("recorder stopped");
							mediaSource.endOfStream();
						}

						_mediaRecorder.ondataavailable = (e) => {
							// e.data 为blob格式
							document.getElementById("value1").innerHTML = e.data.size;

							let reader = new FileReader()
							reader.onloadend = (e) => {
								if (sourceBuffer.updating || queue.length > 0) {
									queue.push(reader.result);
								} else {
									mediaSource.readyState != 'ended' && sourceBuffer.appendBuffer(reader.result);
								}
							}
							reader.readAsArrayBuffer(e.data);
						}

					})
					.catch((err) => {
						console.log('The following error occured: ' + err);
					})
			}
		}

		document.getElementById("pause").onclick = () => {
			_mediaRecorder && _mediaRecorder.state != 'inactive' && _mediaRecorder.pause();
			console.log(_mediaRecorder.state);
			console.log("recorder pause");
		}

		document.getElementById("resume").onclick = () => {
			_mediaRecorder && _mediaRecorder.state != 'inactive' && _mediaRecorder.resume();
			console.log(_mediaRecorder.state);
			console.log("recorder resume");
		}

		document.getElementById("stop").onclick = () => {
			_mediaRecorder && _mediaRecorder.state != 'inactive' && _mediaRecorder.stop();
			console.log(_mediaRecorder.state);
			console.log("recorder stopped");
			_stream && _stream.getTracks().forEach((track) => {
				track.stop();
			});
		}

		function createMediaSource() {
			if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
				mediaSource = new MediaSource;
				video1.src = URL.createObjectURL(mediaSource);
				//document.getElementById("audio1").src = URL.createObjectURL(mediaSource);
				mediaSource.addEventListener('sourceopen', sourceOpen);
			} else {
				console.error('Unsupported MIME type or codec: ', mimeCodec, false);
			}
		}
		function sourceOpen(_) {
			//console.log(this.readyState); // open
			var mediaSource = this;
			sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);

			// sourceBuffer.addEventListener('updatestart', (e) => { console.log('updatestart: ' + mediaSource.readyState); });
			// sourceBuffer.addEventListener('updating', (e) => { console.log('updating: ' + mediaSource.readyState); });
			// sourceBuffer.addEventListener('update', (e) => { console.log('update: ' + mediaSource.readyState); });
			sourceBuffer.addEventListener('updateend', (e) => {
				console.log('updateend: ' + mediaSource.readyState);
				// 只缓存12秒
				if (video1.buffered.length && video1.buffered.end(0) - video1.buffered.start(0) > 12) {
					sourceBuffer.remove(0, video1.buffered.end(0) - 12)
				}
			});
			sourceBuffer.addEventListener('error', (e) => { console.log('error: ' + mediaSource.readyState, e); });
			sourceBuffer.addEventListener('abort', (e) => { console.log('abort: ' + mediaSource.readyState); });

			sourceBuffer.addEventListener('update', () => { // Note: Have tried 'updateend'
				if (queue.length > 0 && !sourceBuffer.updating) {
					sourceBuffer.appendBuffer(queue.shift());
				}
			});
		};
		video1.addEventListener("canplay", () => { video1.play(); });
	</script>
</body>

</html>